Introduction

The following is intended as a set of tips for people coding or about to code an R package.

Extensive guides to writing an R package are given

these tips are meant to supplement them.

Tips

Create

  • Create a new package by first installing devtools and then running
devtools::create()
  • This will generate
> devtools::create(path = ".")
✓ Setting active project to '/Users/tom/Downloads/mynewpackage'
✓ Creating 'R/'
✓ Writing 'DESCRIPTION'
Package: mynewpackage
Title: What the Package Does (One Line, Title Case)
Version: 0.0.0.9000
Authors@R (parsed):
    * First Last <first.last@example.com> [aut, cre] OUR-ORCID-ID)
Description: What the package does (one paragraph).
License: `use_mit_license()`, `use_gpl3_license()` or friends to
    pick a license
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.2
✓ Writing 'NAMESPACE'
✓ Writing 'mynewpackage.Rproj'
✓ Adding '^mynewpackage\\.Rproj$' to '.Rbuildignore'
✓ Adding '.Rproj.user' to '.gitignore'
✓ Adding '^\\.Rproj\\.user$' to '.Rbuildignore'
✓ Setting active project to '<no active project>'
  • As seen in RStudio File pane (nb. even though there is a .gitignore file this folder is not initialised as a git repo yet - look for .git folder)
  • Double click the .Rproj file to open in RStudio as an RStudio project - this gives you the very helpful Build pane
  • If you are working on an existing package define folder/repo as an RStudio project (.Rproj file) to get the very helpful Build pane
  • Initialise the folder as a Git repo: usethis::use_git()
  • Now edit/complete the obvious fields in the DESCRIPTION file
  • Not all of these files will be put into our package, e.g., when we build the source package at the moment only the DESCRIPTION and NAMESPACE files are included (as our R and man directories are empty)
  • To prove the contents of the .tar.gz file are currently only

States

  • Confusingly R packages can be in different states
  • Files change location depending upon state - see here
  • Source - the files you code - what’s in your new folder / GitHub repo
  • (Source) bundled - the .tar.gz file of your source files excluding those in .Rbuildignore - what you need to submit to CRAN
  • Binary - the ‘compiled’ version of the bundled package compressed (.zip on Windows)
    • the helpfiles converted from .Rd to .html
    • Vignettes compiled to .html
    • any C code etc. will have been compiled
    • /R no longer contains .R files, now contains 3 files that store the parsed functions in an efficient file format - result of loading all the R code and then saving the functions with save()
  • Installed - the uncompressed zip file in the correct library folder
  • In memory - the binary, installed package loaded via library()

.R and helpfiles

  • .R files contain the functions your packge will provide
  • Don’t code helpfiles (.Rd files in the man folder) yourself
  • Use Roxygen syntax through the roxygen2 package
    • Roxygen syntax lines begin #'
  • To additionally use markdown syntax within roxygen syntax add the following line to your DESCRIPTION file
Roxygen: list(markdown = TRUE)
  • Your .R files will look something like the following, i.e., roxygen code for the helpfile for a function before the actual R code of the function
#' The length of a string
#'
#' Technically this returns the number of "code points", in a string. One
#' code point usually corresponds to one character, but not always. For example,
#' an u with a umlaut might be represented as a single character or as the
#' combination a u and an umlaut.
#'
#' @inheritParams str_detect
#' @return A numeric vector giving number of characters (code points) in each
#'    element of the character vector. Missing string have missing length.
#' @seealso [stringi::stri_length()] which this function wraps.
#' @export
#' @examples
#' str_length(letters)
#' str_length(NA)
#' str_length(factor("abc"))
#' str_length(c("i", "like", "programming", NA))
str_length <- function(string) {
  ...
}
  • Running devtools::document() will generate your .Rd files from your .R files

Classes

  • Use classes for your functions (S3 easiest system; S4, RC)
  • Under S3 your class probably needs print, summary, print.summary methods, defined as e.g. 
# A function defines a new S3 class
# This function shouldn't really print anything to the R console when run
#' @export
myfun <- function(...){
  ...
  class(output) <- "mynewclass"
  # alternatively given objects can have multiple classes, append your new class
  # class(output) <- append("mynewclass", class(output))
  output
}

# print method for objects of class mynewclass
#' @export
print.mynewclass <- function(x, digits = max(3, getOption("digits") - 3), ...){
  ...
  invisible(x)
}

# summary method for objects of class mynewclass
#' @export
summary.mynewclass <- function(...){
  ...
  class(output) <- "summary.mynewclass"
  output
}

# print method for objects of class summary.mynewclass
#' @export
print.summary.mynewclass <- function(x, digits = max(3, getOption("digits") - 3),...){
  ...
  invisible(x)  
}
  • I recommend only using dots/periods in your function names if you are coding under the S3 class system

NAMESPACE

  • The NAMESPACE file is written by devtools when you run devtools::document() on your correctly formatted helpfiles
  • You must specify to @export functions you want exported from your package!
  • Remember to @importFrom pkgname fn the functions you have used from other packages in the roxygen code before your function
  • Read https://r-pkgs.org/namespace.html many times!
  • Remember what happens when we load a package - an environment of its objects are placed at the top of R’s search path (figure from here)
  • Every package has a package environment (the exported functions) and a namespace environment (both exported and internal functions)

Checking

  • Use R CMD check/devtools::check()/Check button in RStudio Build pane

  • Aim for 0 errors and 0 warnings - this is the minimum requirement for CRAN

  • You can setup a GitHub Action to run this on every commit into repo

  • usethis::use_github_action()/usethis::use_tidy_github_actions() or copy from https://github.com/r-lib/actions

  • Stata and python have no such similarly thorough checking system, so well worth using

  • Think about including tests with testthat or another testing package

  • If your package passes R CMD check then you can build a pkgdown website for your package with one command pkgdown::build_site()

  • Use win-builder for a further check https://win-builder.r-project.org/

  • For both win-builder and CRAN you will need to submit the source version of your package as a .tar.gz file, e.g. in RStudio or issue R CMD build mynewpackage at the command line

    • Note the .tar.gz file is what remotes::install_github() makes then installs if installing from GitHub

Git and GitHub

  • Use a visual git editor, e.g. GitHub Desktop
  • Work in a new branch, especially if working with others
    • Then merge into main/master when you’re sure your changes are what you want
  • When coding, I recommend making your changes
    • Then select relevant changed lines
    • Use sensible commit messages, i.e., do not make one massive commit with commit message “Edits”!
  • I prefer not to keep my git repositories in a folder which is also synchronized with OneDrive/Google Drive, e.g., when you render an R Markdown file the syncing of all the auxiliary files that are created can be very annoying
  • When contributing to a new package, after forking on GitHub, then cloning to your local machine, and opening in RStudio/setting working directory to top level of package run devtools::install_dev_deps() to install all packages under Depends, Imports, and Suggests in its DESCRIPTION file
  • Your repo can contain much more than just your R package
    • It will often additionally contain the static website for the package (made with pkgdown) which can be hosted for free using GitHub Pages/Netlify
    • Remember to exclude the files/folders not the package by adding them to the .Rbuildignore file
  • Some people don’t like others distributing packages through a GitHub repo, because install_github() looks for a change in the hash of the latest commit - but that may have been in a file not included in the source package

System dependencies

  • Source R packages are not totally self-contained if external C libraries (or similar) are required to build it as they are not included in the .tar.gz file
  • To install such a package either download the binary package from CRAN (binary packages for Windows take about 1 day longer to appear after the new source version appears)/RSPM/package manager
    • If you never want this problem always click “No” on this window
  • Or, install the required system dependencies on your computer first, e.g. you may see installation failures such as
> install.packages("openssl", type = "source")
* installing *source* package 'openssl' ...
** package 'openssl' successfully unpacked and MD5 sums checked
Using PKG_CFLAGS=
------------------------- ANTICONF ERROR ---------------------------
Configuration failed because openssl was not found. Try installing:
 * deb: libssl-dev (Debian, Ubuntu, etc)
 * rpm: openssl-devel (Fedora, CentOS, RHEL)
 * csw: libssl_dev (Solaris)
 * brew: openssl@1.1 (Mac OSX)
If openssl is already installed, check that 'pkg-config' is in your
PATH and PKG_CONFIG_PATH contains a openssl.pc file. If pkg-config
is unavailable you can set INCLUDE_DIR and LIB_DIR manually via:
R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'
--------------------------------------------------------------------
ERROR: configuration failed for package 'openssl'
* removing '/usr/local/lib/R/site-library/openssl'
  • Check if a package name is mentioned in the error message. To install it use the following tools for your operating system
  • Windows:
  • macOS:
    • Install Command Line Tools, issue in the Terminal app

      xcode-select --install
    • Most dependencies are available using Homebrew, e.g.,

      brew install ...
  • Linux
    • Use the package manager with your distro, e.g.

      sudo apt-get install ...

LS0tDQp0aXRsZTogIlRpcHMgZm9yIGNvZGluZyBSIHBhY2thZ2VzIg0KYXV0aG9yOiAiVG9tIFBhbG1lciINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgICMgdG9jOiB0cnVlDQogICAgIyB0b2NfZmxvYXQ6DQogICAgIyAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICAjICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCiAgICAjIHRvY19kZXB0aDogMg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICBhbmNob3Jfc2VjdGlvbnM6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCnJlcXVpcmUoIkhtaXNjIikNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gRkFMU0UpDQpgYGANCg0KPCEtLSBgciBIbWlzYzo6aGlkaW5nVE9DKGxldmVscyA9IDIpYCAtLT4NCg0KIyMgSW50cm9kdWN0aW9uDQoNClRoZSBmb2xsb3dpbmcgaXMgaW50ZW5kZWQgYXMgYSBzZXQgb2YgdGlwcyBmb3IgcGVvcGxlIGNvZGluZyBvciBhYm91dCB0byBjb2RlIGFuIFIgcGFja2FnZS4NCg0KRXh0ZW5zaXZlIGd1aWRlcyB0byB3cml0aW5nIGFuIFIgcGFja2FnZSBhcmUgZ2l2ZW4NCg0KLSBhdCBodHRwczovL3ItcGtncy5vcmcvIA0KLSBhbmQgaHR0cHM6Ly9rYnJvbWFuLm9yZy9wa2dfcHJpbWVyLw0KDQp0aGVzZSB0aXBzIGFyZSBtZWFudCB0byBzdXBwbGVtZW50IHRoZW0uDQoNCiMjIFRpcHMgey50YWJzZXQgLnRhYnNldC1waWxscyAudGFic2V0LWZhZGV9IA0KDQojIyMgQ3JlYXRlDQoNCi0gQ3JlYXRlIGEgbmV3IHBhY2thZ2UgYnkgZmlyc3QgaW5zdGFsbGluZyBgZGV2dG9vbHNgIGFuZCB0aGVuIHJ1bm5pbmcNCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9DQpkZXZ0b29sczo6Y3JlYXRlKCkNCmBgYA0KDQotIFRoaXMgd2lsbCBnZW5lcmF0ZQ0KYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0NCj4gZGV2dG9vbHM6OmNyZWF0ZShwYXRoID0gIi4iKQ0K4pyTIFNldHRpbmcgYWN0aXZlIHByb2plY3QgdG8gJy9Vc2Vycy90b20vRG93bmxvYWRzL215bmV3cGFja2FnZScNCuKckyBDcmVhdGluZyAnUi8nDQrinJMgV3JpdGluZyAnREVTQ1JJUFRJT04nDQpQYWNrYWdlOiBteW5ld3BhY2thZ2UNClRpdGxlOiBXaGF0IHRoZSBQYWNrYWdlIERvZXMgKE9uZSBMaW5lLCBUaXRsZSBDYXNlKQ0KVmVyc2lvbjogMC4wLjAuOTAwMA0KQXV0aG9yc0BSIChwYXJzZWQpOg0KICAgICogRmlyc3QgTGFzdCA8Zmlyc3QubGFzdEBleGFtcGxlLmNvbT4gW2F1dCwgY3JlXSBPVVItT1JDSUQtSUQpDQpEZXNjcmlwdGlvbjogV2hhdCB0aGUgcGFja2FnZSBkb2VzIChvbmUgcGFyYWdyYXBoKS4NCkxpY2Vuc2U6IGB1c2VfbWl0X2xpY2Vuc2UoKWAsIGB1c2VfZ3BsM19saWNlbnNlKClgIG9yIGZyaWVuZHMgdG8NCiAgICBwaWNrIGEgbGljZW5zZQ0KRW5jb2Rpbmc6IFVURi04DQpSb3h5Z2VuOiBsaXN0KG1hcmtkb3duID0gVFJVRSkNClJveHlnZW5Ob3RlOiA3LjEuMg0K4pyTIFdyaXRpbmcgJ05BTUVTUEFDRScNCuKckyBXcml0aW5nICdteW5ld3BhY2thZ2UuUnByb2onDQrinJMgQWRkaW5nICdebXluZXdwYWNrYWdlXFwuUnByb2okJyB0byAnLlJidWlsZGlnbm9yZScNCuKckyBBZGRpbmcgJy5ScHJvai51c2VyJyB0byAnLmdpdGlnbm9yZScNCuKckyBBZGRpbmcgJ15cXC5ScHJvalxcLnVzZXIkJyB0byAnLlJidWlsZGlnbm9yZScNCuKckyBTZXR0aW5nIGFjdGl2ZSBwcm9qZWN0IHRvICc8bm8gYWN0aXZlIHByb2plY3Q+Jw0KYGBgDQotIEFzIHNlZW4gaW4gUlN0dWRpbyBGaWxlIHBhbmUgKG5iLiBldmVuIHRob3VnaCB0aGVyZSBpcyBhIGAuZ2l0aWdub3JlYCBmaWxlIHRoaXMgZm9sZGVyIGlzIG5vdCBpbml0aWFsaXNlZCBhcyBhIGdpdCByZXBvIHlldCAtIGxvb2sgZm9yIGAuZ2l0YCBmb2xkZXIpICANCmBgYHtyfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImltZy9kZXZ0b29scy1jcmVhdGUucG5nIikNCmBgYA0KLSBEb3VibGUgY2xpY2sgdGhlIGAuUnByb2pgIGZpbGUgdG8gb3BlbiBpbiBSU3R1ZGlvIGFzIGFuIFJTdHVkaW8gcHJvamVjdCAtIHRoaXMgZ2l2ZXMgeW91IHRoZSB2ZXJ5IGhlbHBmdWwgQnVpbGQgcGFuZQ0KYGBge3J9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1nL3JzdHVkaW8tbXluZXdwYWNrYWdlLnBuZyIpDQpgYGANCi0gSWYgeW91IGFyZSB3b3JraW5nIG9uIGFuIGV4aXN0aW5nIHBhY2thZ2UgZGVmaW5lIGZvbGRlci9yZXBvIGFzIGFuIFJTdHVkaW8gcHJvamVjdCAoYC5ScHJvamAgZmlsZSkgdG8gZ2V0IHRoZSB2ZXJ5IGhlbHBmdWwgQnVpbGQgcGFuZSANCiAgLSBodHRwczovL3N1cHBvcnQucnN0dWRpby5jb20vaGMvZW4tdXMvYXJ0aWNsZXMvMjAwNTI2MjA3LVVzaW5nLVByb2plY3RzDQogIC0gS2V5IGxpbmUgaW4gdGhlIGAuUnByb2pgIGZpbGUgaXM6IGBCdWlsZFR5cGU6IFBhY2thZ2VgIChgTm9uZWAvYFBhY2thZ2VgL2BNYWtlZmlsZWAvYFdlYnNpdGVgL2BDdXN0b21gKSAtIEJ1aWxkIHBhbmUgYXBwZWFycyBmb3IgYWxsIGJ1dCBgTm9uZWANCi0gSW5pdGlhbGlzZSB0aGUgZm9sZGVyIGFzIGEgR2l0IHJlcG86IGB1c2V0aGlzOjp1c2VfZ2l0KClgDQotIE5vdyBlZGl0L2NvbXBsZXRlIHRoZSBvYnZpb3VzIGZpZWxkcyBpbiB0aGUgYERFU0NSSVBUSU9OYCBmaWxlDQotIE5vdCBhbGwgb2YgdGhlc2UgZmlsZXMgd2lsbCBiZSBwdXQgaW50byBvdXIgcGFja2FnZSwgZS5nLiwgd2hlbiB3ZSBidWlsZCB0aGUgc291cmNlIHBhY2thZ2UgYXQgdGhlIG1vbWVudCBvbmx5IHRoZSBgREVTQ1JJUFRJT05gIGFuZCBgTkFNRVNQQUNFYCBmaWxlcyBhcmUgaW5jbHVkZWQgKGFzIG91ciBgUmAgYW5kIGBtYW5gIGRpcmVjdG9yaWVzIGFyZSBlbXB0eSkgIA0KYGBge3J9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1nL2J1aWxkLXNvdXJjZS1wa2ctbXluZXdwYWNrYWdlLnBuZyIpDQpgYGANCi0gVG8gcHJvdmUgdGhlIGNvbnRlbnRzIG9mIHRoZSBgLnRhci5nemAgZmlsZSBhcmUgY3VycmVudGx5IG9ubHkgIA0KYGBge3J9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1nL3NvdXJjZS1jb250ZW50cy1teW5ld3BhY2thZ2UucG5nIikNCmBgYA0KDQojIyMgU3RhdGVzDQoNCi0gQ29uZnVzaW5nbHkgUiBwYWNrYWdlcyBjYW4gYmUgaW4gZGlmZmVyZW50IHN0YXRlcyAgDQpgYGB7cn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJpbWcvY3Jhbi1kb3dubG9hZHMtZXhhbXBsZS5wbmciKQ0KYGBgDQotIEZpbGVzIGNoYW5nZSBsb2NhdGlvbiBkZXBlbmRpbmcgdXBvbiBzdGF0ZSAtIHNlZSBbaGVyZV0oaHR0cHM6Ly9yLXBrZ3Mub3JnL3BhY2thZ2Utc3RydWN0dXJlLXN0YXRlLmh0bWwjYnVuZGxlZC1wYWNrYWdlKQ0KLSBTb3VyY2UgLSB0aGUgZmlsZXMgeW91IGNvZGUgLSB3aGF0J3MgaW4geW91ciBuZXcgZm9sZGVyIC8gR2l0SHViIHJlcG8NCi0gKFNvdXJjZSkgYnVuZGxlZCAtIHRoZSBgLnRhci5nemAgZmlsZSBvZiB5b3VyIHNvdXJjZSBmaWxlcyBleGNsdWRpbmcgdGhvc2UgaW4gYC5SYnVpbGRpZ25vcmVgIC0gd2hhdCB5b3UgbmVlZCB0byBzdWJtaXQgdG8gQ1JBTg0KLSBCaW5hcnkgLSB0aGUgJ2NvbXBpbGVkJyB2ZXJzaW9uIG9mIHRoZSBidW5kbGVkIHBhY2thZ2UgY29tcHJlc3NlZCAoYC56aXBgIG9uIFdpbmRvd3MpDQogIC0gdGhlIGhlbHBmaWxlcyBjb252ZXJ0ZWQgZnJvbSBgLlJkYCB0byBgLmh0bWxgDQogIC0gVmlnbmV0dGVzIGNvbXBpbGVkIHRvIGAuaHRtbGANCiAgLSBhbnkgQyBjb2RlIGV0Yy4gd2lsbCBoYXZlIGJlZW4gY29tcGlsZWQNCiAgLSBgL1JgIG5vIGxvbmdlciBjb250YWlucyBgLlJgIGZpbGVzLCBub3cgY29udGFpbnMgMyBmaWxlcyB0aGF0IHN0b3JlIHRoZSBwYXJzZWQgZnVuY3Rpb25zIGluIGFuIGVmZmljaWVudCBmaWxlIGZvcm1hdCAtIHJlc3VsdCBvZiBsb2FkaW5nIGFsbCB0aGUgUiBjb2RlIGFuZCB0aGVuIHNhdmluZyB0aGUgZnVuY3Rpb25zIHdpdGggYHNhdmUoKWANCiAgICBgYGB7cn0NCiAgICBrbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1nL2Flci1iaW5hcnktci1mb2xkZXIucG5nIikNCiAgICBgYGAgIA0KLSBJbnN0YWxsZWQgLSB0aGUgdW5jb21wcmVzc2VkIHppcCBmaWxlIGluIHRoZSBjb3JyZWN0IGxpYnJhcnkgZm9sZGVyDQotIEluIG1lbW9yeSAtIHRoZSBiaW5hcnksIGluc3RhbGxlZCBwYWNrYWdlIGxvYWRlZCB2aWEgYGxpYnJhcnkoKWANCg0KIyMjIC5SIGFuZCBoZWxwZmlsZXMNCg0KLSBgLlJgIGZpbGVzIGNvbnRhaW4gdGhlIGZ1bmN0aW9ucyB5b3VyIHBhY2tnZSB3aWxsIHByb3ZpZGUNCi0gRG9uJ3QgY29kZSBoZWxwZmlsZXMgKGAuUmRgIGZpbGVzIGluIHRoZSBgbWFuYCBmb2xkZXIpIHlvdXJzZWxmIA0KLSBVc2UgUm94eWdlbiBzeW50YXggdGhyb3VnaCB0aGUgW3JveHlnZW4yIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9yLWxpYi9yb3h5Z2VuMikNCiAgLSBSb3h5Z2VuIHN5bnRheCBsaW5lcyBiZWdpbiBgIycgYA0KLSBUbyBhZGRpdGlvbmFsbHkgdXNlIG1hcmtkb3duIHN5bnRheCB3aXRoaW4gcm94eWdlbiBzeW50YXggYWRkIHRoZSBmb2xsb3dpbmcgbGluZSB0byB5b3VyIGBERVNDUklQVElPTmAgZmlsZQ0KYGBgDQpSb3h5Z2VuOiBsaXN0KG1hcmtkb3duID0gVFJVRSkNCmBgYA0KLSBZb3VyIGAuUmAgZmlsZXMgd2lsbCBsb29rIHNvbWV0aGluZyBsaWtlIHRoZSBmb2xsb3dpbmcsIGkuZS4sIHJveHlnZW4gY29kZSBmb3IgdGhlIGhlbHBmaWxlIGZvciBhIGZ1bmN0aW9uIGJlZm9yZSB0aGUgYWN0dWFsIFIgY29kZSBvZiB0aGUgZnVuY3Rpb24NCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9DQojJyBUaGUgbGVuZ3RoIG9mIGEgc3RyaW5nDQojJw0KIycgVGVjaG5pY2FsbHkgdGhpcyByZXR1cm5zIHRoZSBudW1iZXIgb2YgImNvZGUgcG9pbnRzIiwgaW4gYSBzdHJpbmcuIE9uZQ0KIycgY29kZSBwb2ludCB1c3VhbGx5IGNvcnJlc3BvbmRzIHRvIG9uZSBjaGFyYWN0ZXIsIGJ1dCBub3QgYWx3YXlzLiBGb3IgZXhhbXBsZSwNCiMnIGFuIHUgd2l0aCBhIHVtbGF1dCBtaWdodCBiZSByZXByZXNlbnRlZCBhcyBhIHNpbmdsZSBjaGFyYWN0ZXIgb3IgYXMgdGhlDQojJyBjb21iaW5hdGlvbiBhIHUgYW5kIGFuIHVtbGF1dC4NCiMnDQojJyBAaW5oZXJpdFBhcmFtcyBzdHJfZGV0ZWN0DQojJyBAcmV0dXJuIEEgbnVtZXJpYyB2ZWN0b3IgZ2l2aW5nIG51bWJlciBvZiBjaGFyYWN0ZXJzIChjb2RlIHBvaW50cykgaW4gZWFjaA0KIycgICAgZWxlbWVudCBvZiB0aGUgY2hhcmFjdGVyIHZlY3Rvci4gTWlzc2luZyBzdHJpbmcgaGF2ZSBtaXNzaW5nIGxlbmd0aC4NCiMnIEBzZWVhbHNvIFtzdHJpbmdpOjpzdHJpX2xlbmd0aCgpXSB3aGljaCB0aGlzIGZ1bmN0aW9uIHdyYXBzLg0KIycgQGV4cG9ydA0KIycgQGV4YW1wbGVzDQojJyBzdHJfbGVuZ3RoKGxldHRlcnMpDQojJyBzdHJfbGVuZ3RoKE5BKQ0KIycgc3RyX2xlbmd0aChmYWN0b3IoImFiYyIpKQ0KIycgc3RyX2xlbmd0aChjKCJpIiwgImxpa2UiLCAicHJvZ3JhbW1pbmciLCBOQSkpDQpzdHJfbGVuZ3RoIDwtIGZ1bmN0aW9uKHN0cmluZykgew0KICAuLi4NCn0NCmBgYA0KLSBSdW5uaW5nIGBkZXZ0b29sczo6ZG9jdW1lbnQoKWAgd2lsbCBnZW5lcmF0ZSB5b3VyIGAuUmRgIGZpbGVzIGZyb20geW91ciBgLlJgIGZpbGVzDQoNCiMjIyBDbGFzc2VzICANCiAgDQotIFVzZSBjbGFzc2VzIGZvciB5b3VyIGZ1bmN0aW9ucyAoUzMgZWFzaWVzdCBzeXN0ZW07IFM0LCBSQykNCi0gVW5kZXIgUzMgeW91ciBjbGFzcyBwcm9iYWJseSBuZWVkcyBgcHJpbnRgLCBgc3VtbWFyeWAsIGBwcmludC5zdW1tYXJ5YCBtZXRob2RzLCBkZWZpbmVkIGFzIGUuZy4gDQpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQ0KIyBBIGZ1bmN0aW9uIGRlZmluZXMgYSBuZXcgUzMgY2xhc3MNCiMgVGhpcyBmdW5jdGlvbiBzaG91bGRuJ3QgcmVhbGx5IHByaW50IGFueXRoaW5nIHRvIHRoZSBSIGNvbnNvbGUgd2hlbiBydW4NCiMnIEBleHBvcnQNCm15ZnVuIDwtIGZ1bmN0aW9uKC4uLil7DQogIC4uLg0KICBjbGFzcyhvdXRwdXQpIDwtICJteW5ld2NsYXNzIg0KICAjIGFsdGVybmF0aXZlbHkgZ2l2ZW4gb2JqZWN0cyBjYW4gaGF2ZSBtdWx0aXBsZSBjbGFzc2VzLCBhcHBlbmQgeW91ciBuZXcgY2xhc3MNCiAgIyBjbGFzcyhvdXRwdXQpIDwtIGFwcGVuZCgibXluZXdjbGFzcyIsIGNsYXNzKG91dHB1dCkpDQogIG91dHB1dA0KfQ0KDQojIHByaW50IG1ldGhvZCBmb3Igb2JqZWN0cyBvZiBjbGFzcyBteW5ld2NsYXNzDQojJyBAZXhwb3J0DQpwcmludC5teW5ld2NsYXNzIDwtIGZ1bmN0aW9uKHgsIGRpZ2l0cyA9IG1heCgzLCBnZXRPcHRpb24oImRpZ2l0cyIpIC0gMyksIC4uLil7DQogIC4uLg0KICBpbnZpc2libGUoeCkNCn0NCg0KIyBzdW1tYXJ5IG1ldGhvZCBmb3Igb2JqZWN0cyBvZiBjbGFzcyBteW5ld2NsYXNzDQojJyBAZXhwb3J0DQpzdW1tYXJ5Lm15bmV3Y2xhc3MgPC0gZnVuY3Rpb24oLi4uKXsNCiAgLi4uDQogIGNsYXNzKG91dHB1dCkgPC0gInN1bW1hcnkubXluZXdjbGFzcyINCiAgb3V0cHV0DQp9DQoNCiMgcHJpbnQgbWV0aG9kIGZvciBvYmplY3RzIG9mIGNsYXNzIHN1bW1hcnkubXluZXdjbGFzcw0KIycgQGV4cG9ydA0KcHJpbnQuc3VtbWFyeS5teW5ld2NsYXNzIDwtIGZ1bmN0aW9uKHgsIGRpZ2l0cyA9IG1heCgzLCBnZXRPcHRpb24oImRpZ2l0cyIpIC0gMyksLi4uKXsNCiAgLi4uDQogIGludmlzaWJsZSh4KSAgDQp9DQpgYGANCi0gSSByZWNvbW1lbmQgb25seSB1c2luZyBkb3RzL3BlcmlvZHMgaW4geW91ciBmdW5jdGlvbiBuYW1lcyBpZiB5b3UgYXJlIGNvZGluZyB1bmRlciB0aGUgUzMgY2xhc3Mgc3lzdGVtDQoNCiMjIyBOQU1FU1BBQ0UNCiAgDQotIFRoZSBgTkFNRVNQQUNFYCBmaWxlIGlzIHdyaXR0ZW4gYnkgZGV2dG9vbHMgd2hlbiB5b3UgcnVuIGBkZXZ0b29sczo6ZG9jdW1lbnQoKWAgb24geW91ciBjb3JyZWN0bHkgZm9ybWF0dGVkIGhlbHBmaWxlcw0KLSBZb3UgbXVzdCBzcGVjaWZ5IHRvIGBAZXhwb3J0YCBmdW5jdGlvbnMgeW91IHdhbnQgZXhwb3J0ZWQgZnJvbSB5b3VyIHBhY2thZ2UhDQotIFJlbWVtYmVyIHRvIGBAaW1wb3J0RnJvbSBwa2duYW1lIGZuYCB0aGUgZnVuY3Rpb25zIHlvdSBoYXZlIHVzZWQgZnJvbSBvdGhlciBwYWNrYWdlcyBpbiB0aGUgcm94eWdlbiBjb2RlIGJlZm9yZSB5b3VyIGZ1bmN0aW9uDQotIFJlYWQgaHR0cHM6Ly9yLXBrZ3Mub3JnL25hbWVzcGFjZS5odG1sIG1hbnkgdGltZXMhDQotIFJlbWVtYmVyIHdoYXQgaGFwcGVucyB3aGVuIHdlIGxvYWQgYSBwYWNrYWdlIC0gYW4gZW52aXJvbm1lbnQgb2YgaXRzIG9iamVjdHMgYXJlIHBsYWNlZCBhdCB0aGUgdG9wIG9mIFIncyBzZWFyY2ggcGF0aCAoZmlndXJlIGZyb20gW2hlcmVdKGh0dHBzOi8vYWR2LXIuaGFkbGV5Lm56L2Vudmlyb25tZW50cy5odG1sI3NwZWNpYWwtZW52aXJvbm1lbnRzKSkgIA0KYGBge3J9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1nL2Vudmlyb25tZW50cy5wbmciKQ0KYGBgDQotIEV2ZXJ5IHBhY2thZ2UgaGFzIGEgcGFja2FnZSBlbnZpcm9ubWVudCAodGhlIGV4cG9ydGVkIGZ1bmN0aW9ucykgYW5kIGEgbmFtZXNwYWNlIGVudmlyb25tZW50IChib3RoIGV4cG9ydGVkIGFuZCBpbnRlcm5hbCBmdW5jdGlvbnMpDQpgYGB7cn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJpbWcvbmFtZXNwYWNlcy1hbmQtZW52aXJvbm1lbnRzLnBuZyIpDQpgYGANCg0KIyMjIENoZWNraW5nICANCiAgDQotIFVzZSBgUiBDTUQgY2hlY2tgL2BkZXZ0b29sczo6Y2hlY2soKWAvQ2hlY2sgYnV0dG9uIGluIFJTdHVkaW8gQnVpbGQgcGFuZQ0KLSBBaW0gZm9yIDAgZXJyb3JzIGFuZCAwIHdhcm5pbmdzIC0gdGhpcyBpcyB0aGUgbWluaW11bSByZXF1aXJlbWVudCBmb3IgQ1JBTiAgDQpgYGB7cn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCdpbWcvci1jbWQtY2hlY2stb3V0cHV0LnBuZycpDQpgYGANCg0KLSBZb3UgY2FuIHNldHVwIGEgR2l0SHViIEFjdGlvbiB0byBydW4gdGhpcyBvbiBldmVyeSBjb21taXQgaW50byByZXBvDQotIGB1c2V0aGlzOjp1c2VfZ2l0aHViX2FjdGlvbigpYC9gdXNldGhpczo6dXNlX3RpZHlfZ2l0aHViX2FjdGlvbnMoKWAgb3IgY29weSBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9yLWxpYi9hY3Rpb25zDQotIFN0YXRhIGFuZCBweXRob24gaGF2ZSBubyBzdWNoIHNpbWlsYXJseSB0aG9yb3VnaCBjaGVja2luZyBzeXN0ZW0sIHNvIHdlbGwgd29ydGggdXNpbmcNCi0gVGhpbmsgYWJvdXQgaW5jbHVkaW5nIHRlc3RzIHdpdGggW3Rlc3R0aGF0XShodHRwczovL3Rlc3R0aGF0LnItbGliLm9yZy8pIG9yIGFub3RoZXIgdGVzdGluZyBwYWNrYWdlDQotIElmIHlvdXIgcGFja2FnZSBwYXNzZXMgYFIgQ01EIGNoZWNrYCB0aGVuIHlvdSBjYW4gYnVpbGQgYSBwa2dkb3duIHdlYnNpdGUgZm9yIHlvdXIgcGFja2FnZSB3aXRoIG9uZSBjb21tYW5kIGBwa2dkb3duOjpidWlsZF9zaXRlKClgDQotIFVzZSB3aW4tYnVpbGRlciBmb3IgYSBmdXJ0aGVyIGNoZWNrIGh0dHBzOi8vd2luLWJ1aWxkZXIuci1wcm9qZWN0Lm9yZy8gIA0KYGBge3J9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1nL3dpbi1idWlsZGVyLXNjcmVlbnNob3QucG5nIikNCmBgYA0KLSBGb3IgYm90aCB3aW4tYnVpbGRlciBhbmQgQ1JBTiB5b3Ugd2lsbCBuZWVkIHRvIHN1Ym1pdCB0aGUgc291cmNlIHZlcnNpb24gb2YgeW91ciBwYWNrYWdlIGFzIGEgYC50YXIuZ3pgIGZpbGUsIGUuZy4gaW4gUlN0dWRpbyBvciBpc3N1ZSBgUiBDTUQgYnVpbGQgbXluZXdwYWNrYWdlYCBhdCB0aGUgY29tbWFuZCBsaW5lICANCiAgLSBOb3RlIHRoZSBgLnRhci5nemAgZmlsZSBpcyB3aGF0IGByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigpYCBtYWtlcyB0aGVuIGluc3RhbGxzIGlmIGluc3RhbGxpbmcgZnJvbSBHaXRIdWIgIA0KYGBge3J9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1nL3NhdmUtYXMtc291cmNlLXBhY2thZ2Utb3V0cHV0LnBuZyIpDQpgYGANCg0KIyMjIEdpdCBhbmQgR2l0SHViDQoNCi0gVXNlIGEgdmlzdWFsIGdpdCBlZGl0b3IsIGUuZy4gW0dpdEh1YiBEZXNrdG9wXShodHRwczovL2Rlc2t0b3AuZ2l0aHViLmNvbS8pDQotIFdvcmsgaW4gYSBuZXcgYnJhbmNoLCBlc3BlY2lhbGx5IGlmIHdvcmtpbmcgd2l0aCBvdGhlcnMNCiAgLSBUaGVuIG1lcmdlIGludG8gbWFpbi9tYXN0ZXIgd2hlbiB5b3UncmUgc3VyZSB5b3VyIGNoYW5nZXMgYXJlIHdoYXQgeW91IHdhbnQNCi0gV2hlbiBjb2RpbmcsIEkgcmVjb21tZW5kIG1ha2luZyB5b3VyIGNoYW5nZXMgDQogIC0gVGhlbiBzZWxlY3QgcmVsZXZhbnQgY2hhbmdlZCBsaW5lcyANCiAgLSBVc2Ugc2Vuc2libGUgY29tbWl0IG1lc3NhZ2VzLCBpLmUuLCBkbyBub3QgbWFrZSBvbmUgbWFzc2l2ZSBjb21taXQgd2l0aCBjb21taXQgbWVzc2FnZSAiRWRpdHMiISAgDQpgYGB7cn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJpbWcvZ2l0aHViLWRlc2t0b3Atc2VsZWN0LWxpbmVzLnBuZyIpDQpgYGAgIA0KICANCi0gSSBwcmVmZXIgbm90IHRvIGtlZXAgbXkgZ2l0IHJlcG9zaXRvcmllcyBpbiBhIGZvbGRlciB3aGljaCBpcyBhbHNvIHN5bmNocm9uaXplZCB3aXRoIE9uZURyaXZlL0dvb2dsZSBEcml2ZSwgZS5nLiwgd2hlbiB5b3UgcmVuZGVyIGFuIFIgTWFya2Rvd24gZmlsZSB0aGUgc3luY2luZyBvZiBhbGwgdGhlIGF1eGlsaWFyeSBmaWxlcyB0aGF0IGFyZSBjcmVhdGVkIGNhbiBiZSB2ZXJ5IGFubm95aW5nDQotIFdoZW4gY29udHJpYnV0aW5nIHRvIGEgbmV3IHBhY2thZ2UsIGFmdGVyIGZvcmtpbmcgb24gR2l0SHViLCB0aGVuIGNsb25pbmcgdG8geW91ciBsb2NhbCBtYWNoaW5lLCBhbmQgb3BlbmluZyBpbiBSU3R1ZGlvL3NldHRpbmcgd29ya2luZyBkaXJlY3RvcnkgdG8gdG9wIGxldmVsIG9mIHBhY2thZ2UgcnVuIGBkZXZ0b29sczo6aW5zdGFsbF9kZXZfZGVwcygpYCB0byBpbnN0YWxsIGFsbCBwYWNrYWdlcyB1bmRlciBEZXBlbmRzLCBJbXBvcnRzLCBhbmQgU3VnZ2VzdHMgaW4gaXRzIGBERVNDUklQVElPTmAgZmlsZQ0KLSBZb3VyIHJlcG8gY2FuIGNvbnRhaW4gbXVjaCBtb3JlIHRoYW4ganVzdCB5b3VyIFIgcGFja2FnZQ0KICAtIEl0IHdpbGwgb2Z0ZW4gYWRkaXRpb25hbGx5IGNvbnRhaW4gdGhlIHN0YXRpYyB3ZWJzaXRlIGZvciB0aGUgcGFja2FnZSAobWFkZSB3aXRoIHBrZ2Rvd24pIHdoaWNoIGNhbiBiZSBob3N0ZWQgZm9yIGZyZWUgdXNpbmcgR2l0SHViIFBhZ2VzL05ldGxpZnkNCiAgLSBSZW1lbWJlciB0byBleGNsdWRlIHRoZSBmaWxlcy9mb2xkZXJzIG5vdCB0aGUgcGFja2FnZSBieSBhZGRpbmcgdGhlbSB0byB0aGUgYC5SYnVpbGRpZ25vcmVgIGZpbGUNCi0gU29tZSBwZW9wbGUgZG9uJ3QgbGlrZSBvdGhlcnMgZGlzdHJpYnV0aW5nIHBhY2thZ2VzIHRocm91Z2ggYSBHaXRIdWIgcmVwbywgYmVjYXVzZSBgaW5zdGFsbF9naXRodWIoKWAgbG9va3MgZm9yIGEgY2hhbmdlIGluIHRoZSBoYXNoIG9mIHRoZSBsYXRlc3QgY29tbWl0IC0gYnV0IHRoYXQgbWF5IGhhdmUgYmVlbiBpbiBhIGZpbGUgbm90IGluY2x1ZGVkIGluIHRoZSBzb3VyY2UgcGFja2FnZQ0KDQojIyMgU3lzdGVtIGRlcGVuZGVuY2llcw0KDQotIFNvdXJjZSBSIHBhY2thZ2VzIGFyZSBub3QgdG90YWxseSBzZWxmLWNvbnRhaW5lZCBpZiBleHRlcm5hbCBDIGxpYnJhcmllcyAob3Igc2ltaWxhcikgYXJlIHJlcXVpcmVkIHRvIGJ1aWxkIGl0IGFzIHRoZXkgYXJlIG5vdCBpbmNsdWRlZCBpbiB0aGUgYC50YXIuZ3pgIGZpbGUNCi0gVG8gaW5zdGFsbCBzdWNoIGEgcGFja2FnZSBlaXRoZXIgZG93bmxvYWQgdGhlIGJpbmFyeSBwYWNrYWdlIGZyb20gQ1JBTiAoYmluYXJ5IHBhY2thZ2VzIGZvciBXaW5kb3dzIHRha2UgYWJvdXQgMSBkYXkgbG9uZ2VyIHRvIGFwcGVhciBhZnRlciB0aGUgbmV3IHNvdXJjZSB2ZXJzaW9uIGFwcGVhcnMpL1JTUE0vcGFja2FnZSBtYW5hZ2VyDQogIC0gSWYgeW91IG5ldmVyIHdhbnQgdGhpcyBwcm9ibGVtIGFsd2F5cyBjbGljayAiTm8iIG9uIHRoaXMgd2luZG93ICANCiAgICBgYGB7cn0NCiAgICBrbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1nL2RvLXlvdS13YW50LXRvLWluc3RhbGwtZnJvbS1zb3VyY2VzLnBuZyIpDQogICAgYGBgDQotIE9yLCBpbnN0YWxsIHRoZSByZXF1aXJlZCBzeXN0ZW0gZGVwZW5kZW5jaWVzIG9uIHlvdXIgY29tcHV0ZXIgZmlyc3QsIGUuZy4geW91IG1heSBzZWUgaW5zdGFsbGF0aW9uIGZhaWx1cmVzIHN1Y2ggYXMgIA0KYGBgDQo+IGluc3RhbGwucGFja2FnZXMoIm9wZW5zc2wiLCB0eXBlID0gInNvdXJjZSIpDQoqIGluc3RhbGxpbmcgKnNvdXJjZSogcGFja2FnZSAnb3BlbnNzbCcgLi4uDQoqKiBwYWNrYWdlICdvcGVuc3NsJyBzdWNjZXNzZnVsbHkgdW5wYWNrZWQgYW5kIE1ENSBzdW1zIGNoZWNrZWQNClVzaW5nIFBLR19DRkxBR1M9DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIEFOVElDT05GIEVSUk9SIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KQ29uZmlndXJhdGlvbiBmYWlsZWQgYmVjYXVzZSBvcGVuc3NsIHdhcyBub3QgZm91bmQuIFRyeSBpbnN0YWxsaW5nOg0KICogZGViOiBsaWJzc2wtZGV2IChEZWJpYW4sIFVidW50dSwgZXRjKQ0KICogcnBtOiBvcGVuc3NsLWRldmVsIChGZWRvcmEsIENlbnRPUywgUkhFTCkNCiAqIGNzdzogbGlic3NsX2RldiAoU29sYXJpcykNCiAqIGJyZXc6IG9wZW5zc2xAMS4xIChNYWMgT1NYKQ0KSWYgb3BlbnNzbCBpcyBhbHJlYWR5IGluc3RhbGxlZCwgY2hlY2sgdGhhdCAncGtnLWNvbmZpZycgaXMgaW4geW91cg0KUEFUSCBhbmQgUEtHX0NPTkZJR19QQVRIIGNvbnRhaW5zIGEgb3BlbnNzbC5wYyBmaWxlLiBJZiBwa2ctY29uZmlnDQppcyB1bmF2YWlsYWJsZSB5b3UgY2FuIHNldCBJTkNMVURFX0RJUiBhbmQgTElCX0RJUiBtYW51YWxseSB2aWE6DQpSIENNRCBJTlNUQUxMIC0tY29uZmlndXJlLXZhcnM9J0lOQ0xVREVfRElSPS4uLiBMSUJfRElSPS4uLicNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpFUlJPUjogY29uZmlndXJhdGlvbiBmYWlsZWQgZm9yIHBhY2thZ2UgJ29wZW5zc2wnDQoqIHJlbW92aW5nICcvdXNyL2xvY2FsL2xpYi9SL3NpdGUtbGlicmFyeS9vcGVuc3NsJw0KYGBgDQotIENoZWNrIGlmIGEgcGFja2FnZSBuYW1lIGlzIG1lbnRpb25lZCBpbiB0aGUgZXJyb3IgbWVzc2FnZS4gVG8gaW5zdGFsbCBpdCB1c2UgdGhlIGZvbGxvd2luZyB0b29scyBmb3IgeW91ciBvcGVyYXRpbmcgc3lzdGVtICANCi0gV2luZG93czogDQogIC0gSW5zdGFsbCBbUlRvb2xzNF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvYmluL3dpbmRvd3MvUnRvb2xzLyksIHRoaXMgcHJvdmlkZXMgdGhlIFtgcGFjbWFuYF0oaHR0cHM6Ly9naXRodWIuY29tL3Itd2luZG93cy9kb2NzL2Jsb2IvbWFzdGVyL3J0b29sczQwLm1kI3N5c3RlbS1saWJyYXJpZXMtYW5kLXBhY21hbikgc3lzdGVtDQotIG1hY09TOiANCiAgLSBJbnN0YWxsIENvbW1hbmQgTGluZSBUb29scywgaXNzdWUgaW4gdGhlIFRlcm1pbmFsIGFwcA0KICAgIGBgYA0KICAgIHhjb2RlLXNlbGVjdCAtLWluc3RhbGwNCiAgICBgYGANCiAgLSBNb3N0IGRlcGVuZGVuY2llcyBhcmUgYXZhaWxhYmxlIHVzaW5nIFtIb21lYnJld10oaHR0cHM6Ly9icmV3LnNoLyksIGUuZy4sDQogICAgYGBgDQogICAgYnJldyBpbnN0YWxsIC4uLg0KICAgIGBgYA0KLSBMaW51eA0KICAtIFVzZSB0aGUgcGFja2FnZSBtYW5hZ2VyIHdpdGggeW91ciBkaXN0cm8sIGUuZy4NCiAgICBgYGANCiAgICBzdWRvIGFwdC1nZXQgaW5zdGFsbCAuLi4NCiAgICBgYGANCg0KIyMgey19DQo=